// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © theUltimator5

//@version=6
indicator("Dual View HTF Candlestick Patterns [theUltimator5]", overlay=true, max_labels_count=500, max_lines_count=500, max_boxes_count=250)

// ═══════════════════════════════════════════════════════════════════════
// SECTION 1: USER SETTINGS - TIMEFRAME & DISPLAY
// ═══════════════════════════════════════════════════════════════════════

higher_timeframe = input.timeframe("D", title="Higher Timeframe",
     tooltip="Select timeframe to display (e.g., '60' for 1-hour, '240' for 4-hour, '1D' for daily)")

num_candles_to_show   = input.int(7,   title="Number of HTF Candles",           minval=1,  maxval=10,  tooltip="Number of recent HTF candles to display (1-10)")
candle_width_bars     = input.int(10,  title="Candle Width (bars)",              minval=1,  maxval=100, tooltip="Width of each HTF candle in number of bars")
candle_spacing_bars   = input.int(3,   title="Spacing Between Candles (bars)",   minval=0,  maxval=50,  tooltip="Gap between candles in number of bars")
candle_offset_bars    = input.int(20,  title="Offset from Right Edge (bars)",    minval=0,  maxval=200, tooltip="Distance from current bar to start of first candle")
historical_lookback   = input.int(200, title="Historical Pattern Lookback (HTF Candles)", minval=10, maxval=300,
     tooltip="How many HTF candles to scan backwards for pattern detection on chart (independent of floating candles)")

show_floating_candles           = input.bool(true, "Show Floating HTF Candles",
     tooltip="Display the floating HTF candles on the right side of the chart")
show_pattern_candles_on_chart   = input.bool(true, "Plot HTF Candles on Chart at Pattern Locations",
     tooltip="When patterns are detected, draw the HTF candles directly on the chart at their historical location")

// ═══════════════════════════════════════════════════════════════════════
// SECTION 2: USER SETTINGS - CANDLE COLORS & STYLING
// ═══════════════════════════════════════════════════════════════════════

bullish_fill_color       = input.color(color.lime,             title="Bullish Fill Color",          tooltip="Fill color for bullish candle bodies (close > open)")
bullish_line_color       = input.color(color.lime,             title="Bullish Line Color",           tooltip="Border and wick color for bullish candles")
bearish_fill_color       = input.color(color.red,              title="Bearish Fill Color",           tooltip="Fill color for bearish candle bodies (close < open)")
bearish_line_color       = input.color(color.rgb(255,120,120), title="Bearish Line Color",           tooltip="Border and wick color for bearish candles")
candle_body_transparency = input.int(98,                       title="Candle Body Transparency",     minval=0, maxval=100, tooltip="0 = Solid fill, 100 = Completely transparent (outline only)")
line_thickness           = input.int(2,                        title="Line Thickness",               minval=1, maxval=10,  tooltip="Thickness of candle borders and wicks (1-10)")


show_trend_visuals = input.bool(false,          title="Show Trendline",        group="Trend Visuals",
     tooltip="Overlay trend indicators on the chart based on the selected trend detection method")
ma_line_color     = input.color(color.yellow, title="MA Line Color",            group="Trend Visuals")
ma_line_width     = input.int(2,              title="MA Line Width",            group="Trend Visuals", minval=1, maxval=5)
trend_detection_method = input.string("EMA", title="Trend Detection Method",
     options=["SMA", "EMA"],
     tooltip="Method used to determine if price is in an uptrend or downtrend for pattern detection")
ma_length = input.int(12, title="MA Length", minval=1, maxval=200, tooltip="Period for SMA/EMA trend calculation (default: 12)")

// ═══════════════════════════════════════════════════════════════════════
// SECTION 3: USER SETTINGS - PATTERN VISIBILITY
// ═══════════════════════════════════════════════════════════════════════

pattern_filter = input.string("All Patterns", title="Pattern Filter",
     options=["All Patterns", "2-Candle Only", "3-Candle Only"],
     tooltip="Filter patterns by number of candles involved")

show_evening_star      = input.bool(true,  title="Evening Star",       group="Pattern Toggles", inline="star")
show_morning_star      = input.bool(true,  title="Morning Star",       group="Pattern Toggles", inline="star")
show_shooting_star     = input.bool(true,  title="Shooting Star",      group="Pattern Toggles", inline="shoot")
show_hammer            = input.bool(true,  title="Hammer",             group="Pattern Toggles", inline="shoot")
show_inverted_hammer   = input.bool(true,  title="Inverted Hammer",    group="Pattern Toggles", inline="inv")
show_hanging_man       = input.bool(true,  title="Hanging Man",        group="Pattern Toggles", inline="inv")
show_bearish_harami    = input.bool(false, title="Bearish Harami",     group="Pattern Toggles", inline="harami")
show_bullish_harami    = input.bool(false, title="Bullish Harami",     group="Pattern Toggles", inline="harami")
show_bearish_engulfing = input.bool(true,  title="Bearish Engulfing",  group="Pattern Toggles", inline="engulf")
show_bullish_engulfing = input.bool(true,  title="Bullish Engulfing",  group="Pattern Toggles", inline="engulf")
show_piercing_line     = input.bool(true,  title="Piercing Line",      group="Pattern Toggles", inline="pierce")
show_dark_cloud_cover  = input.bool(true,  title="Dark Cloud Cover",   group="Pattern Toggles", inline="pierce")
show_bullish_kicker    = input.bool(true,  title="Bullish Kicker",     group="Pattern Toggles", inline="kicker")
show_bearish_kicker    = input.bool(true,  title="Bearish Kicker",     group="Pattern Toggles", inline="kicker")
show_bullish_belt      = input.bool(true,  title="Bullish Belt Hold",  group="Pattern Toggles")
show_three_inside_up   = input.bool(true,  title="Three Inside Up",    group="Pattern Toggles", inline="inside")
show_three_inside_down = input.bool(true,  title="Three Inside Down",  group="Pattern Toggles", inline="inside")
show_three_outside_up  = input.bool(true,  title="Three Outside Up",   group="Pattern Toggles", inline="outside")
show_three_outside_down= input.bool(true,  title="Three Outside Down", group="Pattern Toggles", inline="outside")

// ═══════════════════════════════════════════════════════════════════════
// SECTION 4: USER SETTINGS - PATTERN DISPLAY OPTIONS
// ═══════════════════════════════════════════════════════════════════════

pattern_text_size       = input.string("small", title="Pattern Text Size", options=["tiny", "small", "normal", "large"])
show_pattern_info_markers = input.bool(true, title="Show Pattern Info Markers",
     tooltip="Show small info icons (ⓘ) with pattern explanations")

label_color_bullish = input.color(color.lime, title="Bullish Sweep Label Color")
label_color_bearish = input.color(color.red,  title="Bearish Sweep Label Color")

doji_sensitivity = input.float(0.05, title="Doji Sensitivity", minval=0.01, maxval=0.5, step=0.01,
     tooltip="Lower values = more sensitive doji detection. 0.1 = body is 10% or less of total range")


// ═══════════════════════════════════════════════════════════════════════
// SECTION 5: USER SETTINGS - CENTER LABEL
// ═══════════════════════════════════════════════════════════════════════

show_center_label       = input.bool(true,    title="Show Center Label",       group="Center Label Settings", tooltip="Show a label below the center of HTF candles displaying the timeframe")
center_label_text_color = input.color(color.white, title="Label Text Color",   group="Center Label Settings")
center_label_size       = input.string("normal", title="Label Size",           group="Center Label Settings", options=["tiny", "small", "normal", "large"])
center_label_offset     = input.int(3, title="Label Offset Below Candles",     group="Center Label Settings", minval=0, maxval=20,
     tooltip="Distance below lowest point of candles (in price units)")

// ═══════════════════════════════════════════════════════════════════════
// SECTION 6: USER SETTINGS - HORIZONTAL REFERENCE LINES
// ═══════════════════════════════════════════════════════════════════════

show_horizontal_lines  = input.bool(false,       title="Show Horizontal Reference Lines", group="Horizontal Lines",
     tooltip="Draw dotted lines from HTF candle highs/lows back to where they touch the chart")
horizontal_line_color  = input.color(color.gray, title="Line Color",   group="Horizontal Lines")
horizontal_line_width  = input.int(1,            title="Line Width",   group="Horizontal Lines", minval=1, maxval=5)

// ═══════════════════════════════════════════════════════════════════════
// SECTION 7: VARIABLE INITIALIZATION
// ═══════════════════════════════════════════════════════════════════════

new_htf_candle_started = timeframe.change(higher_timeframe)

var array<float> htf_open  = array.new<float>()
var array<float> htf_high  = array.new<float>()
var array<float> htf_low   = array.new<float>()
var array<float> htf_close = array.new<float>()
var array<float> htf_ma    = array.new<float>()

var array<int> htf_high_bar = array.new<int>()
var array<int> htf_low_bar  = array.new<int>()

var float current_htf_open      = na
var float current_htf_high      = na
var float current_htf_low       = na
var float current_htf_close     = na
var int   current_htf_high_bar  = na
var int   current_htf_low_bar   = na
var int   current_htf_start_bar = na

var array<box>  candle_bodies = array.new<box>()
var array<line> upper_wicks   = array.new<line>()
var array<line> lower_wicks   = array.new<line>()

var array<line> horizontal_high_lines = array.new<line>()
var array<line> horizontal_low_lines  = array.new<line>()

var label center_label = na

var array<bool>   pattern_at_index      = array.new<bool>()
var array<string> pattern_text_at_index = array.new<string>()
var array<label>  pattern_labels        = array.new<label>()
var array<int>    htf_start_bar         = array.new<int>()
var array<int>    htf_start_bar_array   = array.new<int>()
var array<bool>   candle_in_pattern     = array.new<bool>()

var bool drawings_initialized = false

var int htf_barIndex  = na
var int htf_barIndex1 = na
var int htf_barIndex2 = na
var int htf_barIndex3 = na

// ═══════════════════════════════════════════════════════════════════════
// SECTION 8: HELPER FUNCTIONS
// ═══════════════════════════════════════════════════════════════════════

get_htf_open(index)  => array.size(htf_open)  > index ? array.get(htf_open,  array.size(htf_open)  - 1 - index) : na
get_htf_high(index)  => array.size(htf_high)  > index ? array.get(htf_high,  array.size(htf_high)  - 1 - index) : na
get_htf_low(index)   => array.size(htf_low)   > index ? array.get(htf_low,   array.size(htf_low)   - 1 - index) : na
get_htf_close(index) => array.size(htf_close) > index ? array.get(htf_close, array.size(htf_close) - 1 - index) : na
get_htf_ma(index)    => array.size(htf_ma)    > index ? array.get(htf_ma,    array.size(htf_ma)    - 1 - index) : na

get_timeframe_label(tf) =>
    string result = tf
    if tf == "D" or tf == "1D"
        result := "Daily"
    else if tf == "W" or tf == "1W"
        result := "Weekly"
    else if tf == "M" or tf == "1M"
        result := "Monthly"
    else if tf == "60"
        result := "1H"
    else if tf == "120"
        result := "2H"
    else if tf == "180"
        result := "3H"
    else if tf == "240"
        result := "4H"
    else if tf == "360"
        result := "6H"
    else if tf == "480"
        result := "8H"
    else if tf == "720"
        result := "12H"
    result

// ═══════════════════════════════════════════════════════════════════════
// SECTION 9: PATTERN DETECTION
// ═══════════════════════════════════════════════════════════════════════

detect_pattern_at_index(int candle_index) =>
    string pattern_text = ""
    bool   pattern_found = false

    if array.size(htf_close) > candle_index + 1
        float htf_o  = get_htf_open(candle_index)
        float htf_h  = get_htf_high(candle_index)
        float htf_l  = get_htf_low(candle_index)
        float htf_c  = get_htf_close(candle_index)

        float htf_o1 = get_htf_open(candle_index + 1)
        float htf_h1 = get_htf_high(candle_index + 1)
        float htf_l1 = get_htf_low(candle_index + 1)
        float htf_c1 = get_htf_close(candle_index + 1)

        float htf_o2 = get_htf_open(candle_index + 2)
        float htf_h2 = get_htf_high(candle_index + 2)
        float htf_l2 = get_htf_low(candle_index + 2)
        float htf_c2 = get_htf_close(candle_index + 2)

        float htf_upper = na(htf_h1) ? htf_h : math.max(htf_h, htf_h1)
        float htf_lower = na(htf_l1) ? htf_l : math.min(htf_l, htf_l1)

        // Trend is assessed at the candle immediately before the pattern begins.
        // 2-candle patterns start at candle_index+1, so anchor at candle_index+2.
        // 3-candle patterns start at candle_index+2, so anchor at candle_index+3.
        // Two separate pairs of booleans are computed so each pattern uses the right anchor.

        // Trend direction: compare the MA at the candle before the pattern (index+2 for
        // 2-candle patterns, index+3 for 3-candle) against one candle further back (index+3
        // or index+4). A rising MA = uptrend, falling MA = downtrend.
        float ma2 = get_htf_ma(candle_index + 2)
        float ma3 = get_htf_ma(candle_index + 3)
        float ma4 = get_htf_ma(candle_index + 4)

        bool prior_uptrend_2   = not na(ma2) and not na(ma3) and ma2 > ma3
        bool prior_downtrend_2 = not na(ma2) and not na(ma3) and ma2 < ma3
        bool prior_uptrend_3   = not na(ma3) and not na(ma4) and ma3 > ma4
        bool prior_downtrend_3 = not na(ma3) and not na(ma4) and ma3 < ma4

        bool is_all_or_2 = pattern_filter == "All Patterns" or pattern_filter == "2-Candle Only"
        bool is_all_or_3 = pattern_filter == "All Patterns" or pattern_filter == "3-Candle Only"

        bool evening_star = show_evening_star and is_all_or_3 and not na(htf_c2) and
             htf_c2 > htf_o2 and (htf_c2 - htf_o2) > 10 * math.abs(htf_c1 - htf_o1) and
             (htf_o - htf_c) > 10 * math.abs(htf_c1 - htf_o1) and htf_c < htf_o and
             htf_c < (htf_o2 + (htf_c2 - htf_o2) / 2) and htf_o < htf_c1 and
             prior_uptrend_3

        bool morning_star = show_morning_star and is_all_or_3 and not na(htf_c2) and
             htf_c2 < htf_o2 and (htf_o2 - htf_c2) > 10 * math.abs(htf_c1 - htf_o1) and
             (htf_c - htf_o) > 10 * math.abs(htf_c1 - htf_o1) and htf_c > htf_o and
             htf_c > (htf_c2 + (htf_o2 - htf_c2) / 2) and htf_o > htf_c1 and
             prior_downtrend_3

        bool shooting_star = show_shooting_star and is_all_or_2 and
             (htf_h1 - math.max(htf_o1, htf_c1)) >= math.abs(htf_o1 - htf_c1) * 2 and
             math.min(htf_c1, htf_o1) - htf_l1 <= math.abs(htf_o1 - htf_c1) * 0.1 and
             prior_uptrend_2 and htf_o > htf_c

        bool hammer = show_hammer and is_all_or_2 and
             (htf_h1 - htf_l1) > 3 * math.abs(htf_o1 - htf_c1) and
             (htf_c1 - htf_l1) / (.001 + htf_h1 - htf_l1) > 0.6 and
             (htf_o1 - htf_l1) / (.001 + htf_h1 - htf_l1) > 0.6 and
             prior_downtrend_2 and htf_c > htf_o

        bool inverted_hammer = show_inverted_hammer and is_all_or_2 and
             (htf_h1 - htf_l1) > 3 * math.abs(htf_o1 - htf_c1) and
             (htf_h1 - htf_c1) / (.001 + htf_h1 - htf_l1) > 0.6 and
             (htf_h1 - htf_o1) / (.001 + htf_h1 - htf_l1) > 0.6 and
             prior_downtrend_2 and htf_c > htf_o

        bool bearish_harami = show_bearish_harami and is_all_or_2 and not na(htf_c1) and
             htf_c1 > htf_o1 and htf_o > htf_c and htf_o <= htf_c1 and htf_o1 <= htf_c and
             htf_o - htf_c < htf_c1 - htf_o1 and prior_uptrend_2

        bool bullish_harami = show_bullish_harami and is_all_or_2 and not na(htf_o1) and
             htf_o1 > htf_c1 and htf_c > htf_o and htf_c <= htf_o1 and htf_c1 <= htf_o and
             htf_c - htf_o < htf_o1 - htf_c1 and prior_downtrend_2

        bool bearish_engulfing = show_bearish_engulfing and is_all_or_2 and not na(htf_c1) and
             htf_c1 > htf_o1 and htf_o > htf_c and htf_o >= htf_c1 and htf_o1 >= htf_c and
             htf_o - htf_c > htf_c1 - htf_o1 and prior_uptrend_2

        bool bullish_engulfing = show_bullish_engulfing and is_all_or_2 and not na(htf_o1) and
             htf_o1 > htf_c1 and htf_c > htf_o and htf_c >= htf_o1 and htf_c1 >= htf_o and
             htf_c - htf_o > htf_o1 - htf_c1 and prior_downtrend_2

        bool piercing_line = show_piercing_line and is_all_or_2 and not na(htf_c1) and
             htf_c1 < htf_o1 and htf_o < htf_l1 and
             htf_c > htf_c1 + ((htf_o1 - htf_c1) / 2) and htf_c < htf_o1 and
             prior_downtrend_2

        bool bullish_belt = show_bullish_belt and is_all_or_2 and
             htf_l == htf_o and htf_o < htf_lower and htf_o < htf_c and
             htf_c > ((htf_h1 - htf_l1) / 2) + htf_l1

        bool bullish_kicker = show_bullish_kicker and is_all_or_2 and not na(htf_o1) and
             htf_o1 > htf_c1 and htf_o >= htf_o1 and htf_c > htf_o and
             prior_downtrend_2

        bool bearish_kicker = show_bearish_kicker and is_all_or_2 and not na(htf_o1) and
             htf_o1 < htf_c1 and htf_o <= htf_o1 and htf_c <= htf_o and
             prior_uptrend_2

        bool hanging_man = show_hanging_man and is_all_or_2 and
             (htf_h1 - htf_l1 > 3 * math.abs(htf_o1 - htf_c1)) and
             ((htf_c1 - htf_l1) / (.001 + htf_h1 - htf_l1) >= 0.6) and
             ((htf_o1 - htf_l1) / (.001 + htf_h1 - htf_l1) >= 0.6) and
             prior_uptrend_2 and htf_o > htf_c

        bool dark_cloud_cover = show_dark_cloud_cover and is_all_or_2 and not na(htf_c1) and
             htf_c1 > htf_o1 and ((htf_c1 + htf_o1) / 2) > htf_c and
             htf_o > htf_c and htf_o > htf_c1 and htf_c > htf_o1 and
             ((htf_o - htf_c) / (.001 + (htf_h - htf_l)) > 0.6) and prior_uptrend_2

        bool three_inside_up = show_three_inside_up and is_all_or_3 and not na(htf_o2) and
             htf_o2 > htf_c2 and htf_c1 > htf_o1 and
             htf_c1 <= htf_o2 and htf_o1 >= htf_c2 and
             htf_c1 - htf_o1 < htf_o2 - htf_c2 and
             htf_c > htf_o and htf_c > htf_c1 and prior_downtrend_3

        bool three_inside_down = show_three_inside_down and is_all_or_3 and not na(htf_c2) and
             htf_c2 > htf_o2 and htf_o1 > htf_c1 and
             htf_o1 <= htf_c2 and htf_c1 >= htf_o2 and
             htf_o1 - htf_c1 < htf_c2 - htf_o2 and
             htf_o > htf_c and htf_c < htf_c1 and prior_uptrend_3

        bool three_outside_up = show_three_outside_up and is_all_or_3 and not na(htf_o2) and
             htf_o2 > htf_c2 and htf_c1 > htf_o1 and
             htf_c1 >= htf_o2 and htf_c2 >= htf_o1 and
             htf_c1 - htf_o1 > htf_o2 - htf_c2 and
             htf_c > htf_c1 and prior_downtrend_3

        bool three_outside_down = show_three_outside_down and is_all_or_3 and not na(htf_c2) and
             htf_c2 > htf_o2 and htf_o1 > htf_c1 and
             htf_o1 >= htf_c2 and htf_o2 >= htf_c1 and
             htf_o1 - htf_c1 > htf_c2 - htf_o2 and
             htf_c < htf_c1 and prior_uptrend_3

        if evening_star
            pattern_text := pattern_text + (pattern_text == "" ? "" : "\n") + "Evening Star"
        if morning_star
            pattern_text := pattern_text + (pattern_text == "" ? "" : "\n") + "Morning Star"
        if shooting_star
            pattern_text := pattern_text + (pattern_text == "" ? "" : "\n") + "Shooting Star"
        if hammer
            pattern_text := pattern_text + (pattern_text == "" ? "" : "\n") + "Hammer"
        if inverted_hammer
            pattern_text := pattern_text + (pattern_text == "" ? "" : "\n") + "Inverted Hammer"
        if bearish_harami
            pattern_text := pattern_text + (pattern_text == "" ? "" : "\n") + "Bearish Harami"
        if bullish_harami
            pattern_text := pattern_text + (pattern_text == "" ? "" : "\n") + "Bullish Harami"
        if bearish_engulfing
            pattern_text := pattern_text + (pattern_text == "" ? "" : "\n") + "Bearish Engulfing"
        if bullish_engulfing
            pattern_text := pattern_text + (pattern_text == "" ? "" : "\n") + "Bullish Engulfing"
        if piercing_line
            pattern_text := pattern_text + (pattern_text == "" ? "" : "\n") + "Piercing Line"
        if bullish_belt
            pattern_text := pattern_text + (pattern_text == "" ? "" : "\n") + "Bullish Belt"
        if bullish_kicker
            pattern_text := pattern_text + (pattern_text == "" ? "" : "\n") + "Bullish Kicker"
        if bearish_kicker
            pattern_text := pattern_text + (pattern_text == "" ? "" : "\n") + "Bearish Kicker"
        if hanging_man
            pattern_text := pattern_text + (pattern_text == "" ? "" : "\n") + "Hanging Man"
        if dark_cloud_cover
            pattern_text := pattern_text + (pattern_text == "" ? "" : "\n") + "Dark Cloud Cover"
        if three_inside_up
            pattern_text := pattern_text + (pattern_text == "" ? "" : "\n") + "Three Inside Up"
        if three_inside_down
            pattern_text := pattern_text + (pattern_text == "" ? "" : "\n") + "Three Inside Down"
        if three_outside_up
            pattern_text := pattern_text + (pattern_text == "" ? "" : "\n") + "Three Outside Up"
        if three_outside_down
            pattern_text := pattern_text + (pattern_text == "" ? "" : "\n") + "Three Outside Down"

        pattern_found := pattern_text != ""

    [pattern_found, pattern_text]

// ═══════════════════════════════════════════════════════════════════════
// SECTION 10: DRAWING INITIALIZATION
// ═══════════════════════════════════════════════════════════════════════

initialize_drawings() =>
    if array.size(candle_bodies) > 0
        for i = 0 to array.size(candle_bodies) - 1
            box.delete(array.get(candle_bodies, i))
        array.clear(candle_bodies)

    if array.size(upper_wicks) > 0
        for i = 0 to array.size(upper_wicks) - 1
            line.delete(array.get(upper_wicks, i))
        array.clear(upper_wicks)

    if array.size(lower_wicks) > 0
        for i = 0 to array.size(lower_wicks) - 1
            line.delete(array.get(lower_wicks, i))
        array.clear(lower_wicks)

    if array.size(horizontal_high_lines) > 0
        for i = 0 to array.size(horizontal_high_lines) - 1
            line.delete(array.get(horizontal_high_lines, i))
        array.clear(horizontal_high_lines)

    if array.size(horizontal_low_lines) > 0
        for i = 0 to array.size(horizontal_low_lines) - 1
            line.delete(array.get(horizontal_low_lines, i))
        array.clear(horizontal_low_lines)

    for i = 0 to num_candles_to_show - 1
        int offset_from_right = candle_offset_bars + (i * (candle_width_bars + candle_spacing_bars))
        int candle_left  = bar_index + 1 + offset_from_right
        int candle_right = candle_left + candle_width_bars
        int wick_center  = math.round((candle_left + candle_right) / 2)

        array.push(candle_bodies, box.new(
             left=candle_left, right=candle_right, top=high, bottom=low,
             bgcolor=color.new(color.gray, 100), border_color=color.gray,
             border_width=line_thickness, xloc=xloc.bar_index))

        array.push(upper_wicks, line.new(
             x1=wick_center, y1=high, x2=wick_center, y2=high,
             color=color.gray, width=line_thickness, xloc=xloc.bar_index))

        array.push(lower_wicks, line.new(
             x1=wick_center, y1=low, x2=wick_center, y2=low,
             color=color.gray, width=line_thickness, xloc=xloc.bar_index))

        array.push(horizontal_high_lines, line.new(
             x1=bar_index, y1=high, x2=wick_center, y2=high,
             color=color.new(color.gray, 100), width=horizontal_line_width,
             style=line.style_dotted, xloc=xloc.bar_index))

        array.push(horizontal_low_lines, line.new(
             x1=bar_index, y1=low, x2=wick_center, y2=low,
             color=color.new(color.gray, 100), width=horizontal_line_width,
             style=line.style_dotted, xloc=xloc.bar_index))

// ═══════════════════════════════════════════════════════════════════════
// SECTION 11: DRAWING UPDATE
// ═══════════════════════════════════════════════════════════════════════

update_drawings() =>
    int   num_candles_completed = array.size(htf_close)
    bool  has_current_candle    = not na(current_htf_open)

    for i = 0 to num_candles_to_show - 1
        int position_index    = num_candles_to_show - 1 - i
        int offset_from_right = candle_offset_bars + (position_index * (candle_width_bars + candle_spacing_bars))
        int candle_left       = bar_index + 1 + offset_from_right
        int candle_right      = candle_left + candle_width_bars
        int wick_center       = math.round((candle_left + candle_right) / 2)

        var float htf_o     = na
        var float htf_h     = na
        var float htf_l     = na
        var float htf_c     = na
        var int   htf_h_bar = na
        var int   htf_l_bar = na
        var int   candle_array_index = na

        if i == 0 and has_current_candle
            htf_o             := current_htf_open
            htf_h             := current_htf_high
            htf_l             := current_htf_low
            htf_c             := current_htf_close
            htf_h_bar         := current_htf_high_bar
            htf_l_bar         := current_htf_low_bar
            candle_array_index := -1
        else
            int array_offset  = has_current_candle ? i - 1 : i
            candle_array_index := array_offset
            int array_index   = num_candles_completed - 1 - array_offset

            if array_index >= 0 and array_index < num_candles_completed
                htf_o     := array.get(htf_open,     array_index)
                htf_h     := array.get(htf_high,     array_index)
                htf_l     := array.get(htf_low,      array_index)
                htf_c     := array.get(htf_close,    array_index)
                htf_h_bar := array.get(htf_high_bar, array_index)
                htf_l_bar := array.get(htf_low_bar,  array_index)

        if not na(htf_o) and not na(htf_h) and not na(htf_l) and not na(htf_c)
            bool  is_bullish = htf_c >= htf_o
            color fill_color = is_bullish ? bullish_fill_color : bearish_fill_color
            color line_color = is_bullish ? bullish_line_color : bearish_line_color

            bool  is_pattern_candle   = candle_array_index >= 0 and candle_array_index < array.size(candle_in_pattern) and array.get(candle_in_pattern, candle_array_index)
            float body_transparency   = is_pattern_candle ? 20 : candle_body_transparency

            box  body       = array.get(candle_bodies, i)
            line upper_wick = array.get(upper_wicks,   i)
            line lower_wick = array.get(lower_wicks,   i)

            box.set_left(body,         candle_left)
            box.set_right(body,        candle_right)
            box.set_top(body,          math.max(htf_o, htf_c))
            box.set_bottom(body,       math.min(htf_o, htf_c))
            box.set_bgcolor(body,      color.new(fill_color, body_transparency))
            box.set_border_color(body, line_color)

            float tolerance = syminfo.mintick * 0.1

            if htf_h > math.max(htf_o, htf_c) + tolerance
                line.set_x1(upper_wick, wick_center)
                line.set_x2(upper_wick, wick_center)
                line.set_y1(upper_wick, math.max(htf_o, htf_c))
                line.set_y2(upper_wick, htf_h)
                line.set_color(upper_wick, line_color)
            else
                line.set_y1(upper_wick, htf_h)
                line.set_y2(upper_wick, htf_h)
                line.set_color(upper_wick, color.new(line_color, 100))

            if htf_l < math.min(htf_o, htf_c) - tolerance
                line.set_x1(lower_wick, wick_center)
                line.set_x2(lower_wick, wick_center)
                line.set_y1(lower_wick, math.min(htf_o, htf_c))
                line.set_y2(lower_wick, htf_l)
                line.set_color(lower_wick, line_color)
            else
                line.set_y1(lower_wick, htf_l)
                line.set_y2(lower_wick, htf_l)
                line.set_color(lower_wick, color.new(line_color, 100))
        else
            box.set_bgcolor(array.get(candle_bodies, i),      color.new(color.gray, 100))
            box.set_border_color(array.get(candle_bodies, i), color.new(color.gray, 100))
            line.set_color(array.get(upper_wicks, i),         color.new(color.gray, 100))
            line.set_color(array.get(lower_wicks, i),         color.new(color.gray, 100))

        if show_horizontal_lines and not na(htf_h) and not na(htf_l) and not na(htf_h_bar) and not na(htf_l_bar)
            line high_line = array.get(horizontal_high_lines, i)
            line.set_x1(high_line, htf_h_bar)
            line.set_x2(high_line, wick_center)
            line.set_y1(high_line, htf_h)
            line.set_y2(high_line, htf_h)
            line.set_color(high_line, horizontal_line_color)

            line low_line = array.get(horizontal_low_lines, i)
            line.set_x1(low_line, htf_l_bar)
            line.set_x2(low_line, wick_center)
            line.set_y1(low_line, htf_l)
            line.set_y2(low_line, htf_l)
            line.set_color(low_line, horizontal_line_color)
        else
            line.set_color(array.get(horizontal_high_lines, i), color.new(color.gray, 100))
            line.set_color(array.get(horizontal_low_lines,  i), color.new(color.gray, 100))

// ═══════════════════════════════════════════════════════════════════════
// SECTION 12: PATTERN EXPLANATION HELPER
// ═══════════════════════════════════════════════════════════════════════

get_pattern_explanation(string pattern_text) =>
    string explanation = "Candlestick pattern detected on higher timeframe candles."
    if      str.contains(pattern_text, "Hammer")
        explanation := "The hammer candlestick pattern is a bullish reversal signal appearing at the bottom of a downtrend, featuring a small body at the top and a long lower shadow at least twice the body's length. It indicates that sellers pushed prices down, but buyers surged to close near the open, signaling a potential shift to an uptrend. The following candle confirmed the reversal with bullish momentum."
    else if str.contains(pattern_text, "Inverted Hammer")
        explanation := "The inverted hammer is a bullish reversal pattern appearing after a downtrend, characterized by a small body at the bottom and a long upper shadow. It shows that buyers attempted to push prices higher, but sellers regained control temporarily. The following bullish candle confirms the reversal, indicating buyers are gaining strength."
    else if str.contains(pattern_text, "Shooting Star")
        explanation := "The shooting star is a bearish reversal pattern appearing after an uptrend, featuring a small body at the bottom with a long upper shadow. It indicates that buyers pushed prices higher during the session, but sellers drove the price back down near the open, suggesting weakening momentum. The following bearish candle confirmed the reversal."
    else if str.contains(pattern_text, "Hanging Man")
        explanation := "The hanging man is a bearish reversal pattern appearing after an uptrend, with a small body at the top and a long lower shadow. It suggests that sellers pushed prices down during the session, though buyers managed to close near the open. The following bearish candle confirms that sellers are taking control and a downtrend may be starting."
    else if str.contains(pattern_text, "Morning Star")
        explanation := "The morning star is a three-candle bullish reversal pattern appearing after a downtrend. It consists of a long bearish candle, followed by a small-bodied candle (showing indecision), and completed by a strong bullish candle that closes well into the first candle's body. This pattern signals that sellers are losing control and buyers are stepping in."
    else if str.contains(pattern_text, "Evening Star")
        explanation := "The evening star is a three-candle bearish reversal pattern appearing after an uptrend. It consists of a long bullish candle, followed by a small-bodied candle (showing indecision), and completed by a strong bearish candle that closes well into the first candle's body. This pattern signals that buyers are losing control and sellers are taking over."
    else if str.contains(pattern_text, "Bullish Engulfing")
        explanation := "The bullish engulfing pattern is a two-candle reversal formation appearing after a downtrend. The first candle is bearish, and the second is a larger bullish candle that completely engulfs the first candle's body. This shows a dramatic shift in sentiment as buyers overwhelm sellers, often signaling the start of an uptrend."
    else if str.contains(pattern_text, "Bearish Engulfing")
        explanation := "The bearish engulfing pattern is a two-candle reversal formation appearing after an uptrend. The first candle is bullish, and the second is a larger bearish candle that completely engulfs the first candle's body. This shows a dramatic shift in sentiment as sellers overwhelm buyers, often signaling the start of a downtrend."
    else if str.contains(pattern_text, "Piercing")
        explanation := "The piercing line is a bullish reversal pattern appearing after a downtrend. It consists of a bearish candle followed by a bullish candle that opens below the prior low but closes above the midpoint of the previous candle's body. This demonstrates that buyers are stepping in with strength, potentially reversing the downtrend."
    else if str.contains(pattern_text, "Dark Cloud")
        explanation := "The dark cloud cover is a bearish reversal pattern appearing after an uptrend. It consists of a bullish candle followed by a bearish candle that opens above the prior high but closes below the midpoint of the previous candle's body. This shows that sellers are stepping in with strength, potentially reversing the uptrend."
    else if str.contains(pattern_text, "Bullish Harami")
        explanation := "The bullish harami is a reversal pattern where a small bullish candle appears completely within the body of the previous larger bearish candle. The name 'harami' means pregnant in Japanese, as the small candle is 'inside' the larger one. This pattern suggests indecision and potential trend reversal as selling pressure weakens."
    else if str.contains(pattern_text, "Bearish Harami")
        explanation := "The bearish harami is a reversal pattern where a small bearish candle appears completely within the body of the previous larger bullish candle. The name 'harami' means pregnant in Japanese, as the small candle is 'inside' the larger one. This pattern suggests indecision and potential trend reversal as buying pressure weakens."
    else if str.contains(pattern_text, "Bullish Kicker")
        explanation := "The bullish kicker is a powerful two-candle reversal pattern appearing after a downtrend. It consists of a bearish candle followed by a bullish candle that gaps up and opens at or above the previous candle's open. This dramatic gap and strong bullish move indicates a sudden and forceful shift from selling to buying pressure."
    else if str.contains(pattern_text, "Bearish Kicker")
        explanation := "The bearish kicker is a powerful two-candle reversal pattern appearing after an uptrend. It consists of a bullish candle followed by a bearish candle that gaps down and opens at or below the previous candle's open. This dramatic gap and strong bearish move indicates a sudden and forceful shift from buying to selling pressure."
    else if str.contains(pattern_text, "Three Inside Up")
        explanation := "The three inside up is a bullish reversal pattern that extends the bullish harami. The first candle is bearish, the second is a smaller bullish candle contained within the first (forming a harami), and the third candle closes green above the second candle's close. This confirms the reversal as buyers gain momentum after the indecision."
    else if str.contains(pattern_text, "Three Inside Down")
        explanation := "The three inside down is a bearish reversal pattern that extends the bearish harami. The first candle is bullish, the second is a smaller bearish candle contained within the first (forming a harami), and the third candle closes red below the second candle's close. This confirms the reversal as sellers gain momentum after the indecision."
    else if str.contains(pattern_text, "Three Outside Up")
        explanation := "The three outside up is a bullish reversal pattern that extends the bullish engulfing pattern. The first candle is bearish, the second is a larger bullish candle that engulfs the first, and the third candle closes above the second candle's close. This strong three-candle advance confirms powerful buying pressure and trend reversal."
    else if str.contains(pattern_text, "Three Outside Down")
        explanation := "The three outside down is a bearish reversal pattern that extends the bearish engulfing pattern. The first candle is bullish, the second is a larger bearish candle that engulfs the first, and the third candle closes below the second candle's close. This strong three-candle decline confirms powerful selling pressure and trend reversal."
    explanation

// ═══════════════════════════════════════════════════════════════════════
// SECTION 13: HTF CANDLE TRACKING
// ═══════════════════════════════════════════════════════════════════════

// Computed here so it's available to store in the htf_ma array below.
float htf_ma_value = request.security(syminfo.tickerid, higher_timeframe,
     trend_detection_method == "EMA" ? ta.ema(close, ma_length) : ta.sma(close, ma_length),
     lookahead=barmerge.lookahead_off)

if not drawings_initialized and barstate.isfirst
    initialize_drawings()
    drawings_initialized := true

var array<box>   historical_candle_boxes        = array.new<box>()
var array<line>  historical_candle_upper_wicks  = array.new<line>()
var array<line>  historical_candle_lower_wicks  = array.new<line>()
var array<label> historical_pattern_labels      = array.new<label>()

var array<int> htf_start_time = array.new<int>()
var array<int> htf_end_time   = array.new<int>()
var int current_htf_start_time = na

if new_htf_candle_started
    if not na(current_htf_open)
        array.push(htf_open,           current_htf_open)
        array.push(htf_high,           current_htf_high)
        array.push(htf_low,            current_htf_low)
        array.push(htf_close,          current_htf_close)
        array.push(htf_ma,             htf_ma_value)
        array.push(htf_high_bar,       current_htf_high_bar)
        array.push(htf_low_bar,        current_htf_low_bar)
        array.push(htf_start_bar_array, current_htf_start_bar)
        array.push(htf_start_time,     current_htf_start_time)
        array.push(htf_end_time,       time[1])

        int candles_to_keep = math.max(num_candles_to_show, ma_length)
        candles_to_keep := math.max(candles_to_keep, historical_lookback + ma_length)

        while array.size(htf_open) > candles_to_keep
            array.shift(htf_open)
            array.shift(htf_high)
            array.shift(htf_low)
            array.shift(htf_close)
            array.shift(htf_ma)
            array.shift(htf_high_bar)
            array.shift(htf_low_bar)
            array.shift(htf_start_bar_array)
            array.shift(htf_start_time)
            array.shift(htf_end_time)

    htf_barIndex3 := htf_barIndex2
    htf_barIndex2 := htf_barIndex1
    htf_barIndex1 := htf_barIndex
    htf_barIndex  := bar_index

    current_htf_open      := open
    current_htf_high      := high
    current_htf_low       := low
    current_htf_close     := close
    current_htf_high_bar  := bar_index
    current_htf_low_bar   := bar_index
    current_htf_start_bar := bar_index
    current_htf_start_time := time

else
    if not na(current_htf_open)
        if high > current_htf_high
            current_htf_high     := high
            current_htf_high_bar := bar_index
        if low < current_htf_low
            current_htf_low      := low
            current_htf_low_bar  := bar_index
        current_htf_close := close

// ═══════════════════════════════════════════════════════════════════════
// SECTION 14: TREND VISUALIZATION
// ═══════════════════════════════════════════════════════════════════════

plot(show_trend_visuals ? htf_ma_value : na,
     title="HTF MA",
     color=ma_line_color,
     linewidth=ma_line_width,
     style=plot.style_stepline)

// ═══════════════════════════════════════════════════════════════════════
// SECTION 15: PATTERN DETECTION & HISTORICAL RENDERING
// ═══════════════════════════════════════════════════════════════════════

get_pattern_candle_count(string pattern_text) =>
    int count = 1
    if str.contains(pattern_text, "Evening Star")    or str.contains(pattern_text, "Morning Star")    or
       str.contains(pattern_text, "Three Inside Up")  or str.contains(pattern_text, "Three Inside Down") or
       str.contains(pattern_text, "Three Outside Up") or str.contains(pattern_text, "Three Outside Down")
        count := 3
    else if str.contains(pattern_text, "Shooting Star") or str.contains(pattern_text, "Harami")     or
             str.contains(pattern_text, "Engulfing")     or str.contains(pattern_text, "Piercing")   or
             str.contains(pattern_text, "Belt")          or str.contains(pattern_text, "Kicker")     or
             str.contains(pattern_text, "Hanging Man")   or str.contains(pattern_text, "Dark Cloud") or
             str.contains(pattern_text, "Hammer")        or str.contains(pattern_text, "Inverted Hammer")
        count := 2
    count

if barstate.islast
    array.clear(pattern_at_index)
    array.clear(pattern_text_at_index)

    if array.size(pattern_labels) > 0
        for i = 0 to array.size(pattern_labels) - 1
            label.delete(array.get(pattern_labels, i))
        array.clear(pattern_labels)

    if array.size(historical_candle_boxes) > 0
        for i = 0 to array.size(historical_candle_boxes) - 1
            box.delete(array.get(historical_candle_boxes, i))
        array.clear(historical_candle_boxes)

    if array.size(historical_candle_upper_wicks) > 0
        for i = 0 to array.size(historical_candle_upper_wicks) - 1
            line.delete(array.get(historical_candle_upper_wicks, i))
        array.clear(historical_candle_upper_wicks)

    if array.size(historical_candle_lower_wicks) > 0
        for i = 0 to array.size(historical_candle_lower_wicks) - 1
            line.delete(array.get(historical_candle_lower_wicks, i))
        array.clear(historical_candle_lower_wicks)

    if array.size(historical_pattern_labels) > 0
        for i = 0 to array.size(historical_pattern_labels) - 1
            label.delete(array.get(historical_pattern_labels, i))
        array.clear(historical_pattern_labels)

    int  num_completed_htf  = array.size(htf_close)
    bool has_current_candle = not na(current_htf_open)
    int  max_candles_to_check = math.min(has_current_candle ? num_candles_to_show - 1 : num_candles_to_show, num_completed_htf)

    array.clear(candle_in_pattern)
    for i = 0 to max_candles_to_check - 1
        array.push(candle_in_pattern, false)

    for candle_idx = 0 to max_candles_to_check - 1
        [pattern_found, pattern_text] = detect_pattern_at_index(candle_idx)
        array.push(pattern_at_index, pattern_found)
        array.push(pattern_text_at_index, pattern_text)

        if pattern_found
            int num_in_pattern = get_pattern_candle_count(pattern_text)
            for offset = 0 to num_in_pattern - 1
                int mark_idx = candle_idx + offset
                if mark_idx < array.size(candle_in_pattern)
                    array.set(candle_in_pattern, mark_idx, true)

    if show_pattern_candles_on_chart
        int historical_max = math.min(historical_lookback, num_completed_htf)

        for candle_idx = 0 to historical_max - 1
            [pattern_found, pattern_text] = detect_pattern_at_index(candle_idx)

            if pattern_found
                int num_in_pattern = get_pattern_candle_count(pattern_text)

                for pattern_candle_offset = num_in_pattern - 1 to 0
                    int historical_idx = candle_idx + pattern_candle_offset

                    if historical_idx < array.size(htf_close) and
                       historical_idx < array.size(htf_start_time) and
                       historical_idx < array.size(htf_end_time)

                        int data_idx = array.size(htf_close) - 1 - historical_idx

                        float O = array.get(htf_open,  data_idx)
                        float H = array.get(htf_high,  data_idx)
                        float L = array.get(htf_low,   data_idx)
                        float C = array.get(htf_close, data_idx)

                        int candle_start_time = array.get(htf_start_time, data_idx)
                        int candle_end_time   = array.get(htf_end_time,   data_idx)
                        int mid_time          = math.floor(math.avg(candle_start_time, candle_end_time))

                        bool  isBull      = C >= O
                        color bodyColor   = isBull ? bullish_fill_color : bearish_fill_color
                        color borderColor = isBull ? bullish_line_color : bearish_line_color

                        array.push(historical_candle_boxes, box.new(
                             left=candle_start_time, right=candle_end_time,
                             top=math.max(O, C), bottom=math.min(O, C),
                             border_color=borderColor, bgcolor=color.new(bodyColor, candle_body_transparency),
                             border_width=line_thickness, xloc=xloc.bar_time))

                        array.push(historical_candle_upper_wicks, line.new(mid_time, H, mid_time, math.max(O, C), xloc.bar_time, extend.none, borderColor, line.style_solid, 2))
                        array.push(historical_candle_lower_wicks, line.new(mid_time, L, mid_time, math.min(O, C), xloc.bar_time, extend.none, borderColor, line.style_solid, 2))

                if candle_idx < array.size(htf_close) and
                   candle_idx < array.size(htf_start_time) and
                   candle_idx < array.size(htf_end_time)

                    int   label_data_idx   = array.size(htf_close) - 1 - candle_idx
                    float label_H          = array.get(htf_high,       label_data_idx)
                    int   label_start_time = array.get(htf_start_time, label_data_idx)
                    int   label_end_time   = array.get(htf_end_time,   label_data_idx)
                    int   label_mid_time   = math.floor(math.avg(label_start_time, label_end_time))

                    bool is_bullish_pattern = str.contains(pattern_text, "Morning Star")    or str.contains(pattern_text, "Hammer")     or
                                             str.contains(pattern_text, "Bullish")          or str.contains(pattern_text, "Piercing")    or
                                             str.contains(pattern_text, "Three Inside Up")  or str.contains(pattern_text, "Three Outside Up")
                    bool is_bearish_pattern = str.contains(pattern_text, "Evening Star")    or str.contains(pattern_text, "Shooting Star") or
                                             str.contains(pattern_text, "Bearish")          or str.contains(pattern_text, "Hanging Man")   or
                                             str.contains(pattern_text, "Dark Cloud")       or str.contains(pattern_text, "Three Inside Down") or
                                             str.contains(pattern_text, "Three Outside Down")

                    color pattern_color = is_bullish_pattern ? color.lime : is_bearish_pattern ? color.red : color.white

                    lbl_size = pattern_text_size == "tiny" ? size.tiny : pattern_text_size == "small" ? size.small : pattern_text_size == "normal" ? size.normal : size.large

                    array.push(historical_pattern_labels, label.new(
                         x=label_mid_time, y=label_H, text=pattern_text,
                         color=color.new(color.white, 100), textcolor=pattern_color,
                         style=label.style_label_down, size=lbl_size, xloc=xloc.bar_time))

                    if show_pattern_info_markers
                        float hist_price_range  = high - low
                        float hist_offset_price = hist_price_range * center_label_offset / 100

                        array.push(historical_pattern_labels, label.new(
                             x=label_mid_time, y=label_H + (hist_offset_price * 0.35),
                             text="ⓘ", tooltip=get_pattern_explanation(pattern_text),
                             color=color.new(color.gray, 85), textcolor=color.gray,
                             style=label.style_circle, size=size.tiny, xloc=xloc.bar_time))

// ═══════════════════════════════════════════════════════════════════════
// SECTION 16: DRAWING UPDATES
// ═══════════════════════════════════════════════════════════════════════

if drawings_initialized and show_floating_candles
    update_drawings()

if barstate.islast and show_floating_candles
    int center_x = na

    int half = num_candles_to_show / 2
    if num_candles_to_show % 2 == 1
        int pos           = num_candles_to_show - 1 - half
        int offset        = candle_offset_bars + (pos * (candle_width_bars + candle_spacing_bars))
        int cl            = bar_index + 1 + offset
        int cr            = cl + candle_width_bars
        center_x         := math.round((cl + cr) / 2)
    else
        int pos_l         = num_candles_to_show - half
        int pos_r         = num_candles_to_show - half - 1
        int off_l         = candle_offset_bars + (pos_l * (candle_width_bars + candle_spacing_bars))
        int off_r         = candle_offset_bars + (pos_r * (candle_width_bars + candle_spacing_bars))
        int cr_l          = bar_index + 1 + off_l + candle_width_bars
        int cl_r          = bar_index + 1 + off_r
        center_x         := math.round((cr_l + cl_r) / 2)

    float lowest_point  = na
    float highest_point = na
    int   num_candles_completed = array.size(htf_close)
    bool  has_current_candle    = not na(current_htf_open)

    for i = 0 to num_candles_to_show - 1
        float htf_l = na
        float htf_h = na

        if i == 0 and has_current_candle
            htf_l := current_htf_low
            htf_h := current_htf_high
        else
            int array_offset = has_current_candle ? i - 1 : i
            int array_index  = num_candles_completed - 1 - array_offset
            if array_index >= 0 and array_index < num_candles_completed
                htf_l := array.get(htf_low,  array_index)
                htf_h := array.get(htf_high, array_index)

        if not na(htf_l)
            lowest_point  := na(lowest_point)  ? htf_l : math.min(lowest_point,  htf_l)
        if not na(htf_h)
            highest_point := na(highest_point) ? htf_h : math.max(highest_point, htf_h)

    if not na(lowest_point) and not na(highest_point)
        float price_range   = high - low
        float offset_price  = price_range * center_label_offset / 100
        float label_y_below = lowest_point - offset_price

        lbl_size = center_label_size == "tiny" ? size.tiny : center_label_size == "small" ? size.small : center_label_size == "normal" ? size.normal : size.large

        if not na(center_label)
            label.delete(center_label)

        if show_center_label
            center_label := label.new(
                 x=center_x, y=label_y_below, text=get_timeframe_label(higher_timeframe),
                 color=color.new(color.white, 100), textcolor=center_label_text_color,
                 style=label.style_none, size=lbl_size, xloc=xloc.bar_index)

        for candle_idx = 0 to array.size(pattern_at_index) - 1
            if array.get(pattern_at_index, candle_idx)
                string pattern_text = array.get(pattern_text_at_index, candle_idx)

                bool is_bullish_pattern = str.contains(pattern_text, "Morning Star")    or str.contains(pattern_text, "Hammer")     or
                                         str.contains(pattern_text, "Bullish")          or str.contains(pattern_text, "Piercing")    or
                                         str.contains(pattern_text, "Three Inside Up")  or str.contains(pattern_text, "Three Outside Up")
                bool is_bearish_pattern = str.contains(pattern_text, "Evening Star")    or str.contains(pattern_text, "Shooting Star") or
                                         str.contains(pattern_text, "Bearish")          or str.contains(pattern_text, "Hanging Man")   or
                                         str.contains(pattern_text, "Dark Cloud")       or str.contains(pattern_text, "Three Inside Down") or
                                         str.contains(pattern_text, "Three Outside Down")

                color pattern_color = is_bullish_pattern ? color.lime : is_bearish_pattern ? color.red : center_label_text_color

                int pattern_i             = has_current_candle ? candle_idx + 1 : candle_idx
                int pattern_position      = num_candles_to_show - 1 - pattern_i
                int p_offset              = candle_offset_bars + (pattern_position * (candle_width_bars + candle_spacing_bars))
                int p_left                = bar_index + 1 + p_offset
                int p_right               = p_left + candle_width_bars
                int p_center              = math.round((p_left + p_right) / 2)
                float candle_high         = get_htf_high(candle_idx)
                float pattern_label_y     = candle_high + offset_price

                array.push(pattern_labels, label.new(
                     x=p_center, y=pattern_label_y, text=pattern_text,
                     color=color.new(color.white, 100), textcolor=pattern_color,
                     style=label.style_none, size=lbl_size, xloc=xloc.bar_index))

                if show_pattern_info_markers
                    array.push(pattern_labels, label.new(
                         x=p_center, y=pattern_label_y + (offset_price * 0.35),
                         text="ⓘ", tooltip=get_pattern_explanation(pattern_text),
                         color=color.new(color.gray, 85), textcolor=color.gray,
                         style=label.style_circle, size=size.tiny, xloc=xloc.bar_index))

if barstate.islast and (not show_center_label or not show_floating_candles)
    if not na(center_label)
        label.delete(center_label)
        center_label := na

    if array.size(pattern_labels) > 0
        for i = 0 to array.size(pattern_labels) - 1
            label.delete(array.get(pattern_labels, i))
        array.clear(pattern_labels)
